/*
 * Copyright 1993-2010 NVIDIA Corporation.  All rights reserved.
 *
 * NVIDIA Corporation and its licensors retain all intellectual property and 
 * proprietary rights in and to this software and related documentation. 
 * Any use, reproduction, disclosure, or distribution of this software 
 * and related documentation without an express license agreement from
 * NVIDIA Corporation is strictly prohibited.
 *
 * Please refer to the applicable NVIDIA end user license agreement (EULA) 
 * associated with this source code for terms and conditions that govern 
 * your use of this NVIDIA software.
 * 
 */


// Utilities and system includes
#include <shrUtils.h>
#include <cutil_inline.h>

#include <cuda_runtime.h>

#ifdef WIN32
#define strcasecmp strcmpi
#endif

#include "quasirandomGenerator_common.h"

////////////////////////////////////////////////////////////////////////////////
// CPU code
////////////////////////////////////////////////////////////////////////////////
extern "C" void initQuasirandomGenerator(unsigned int table[QRNG_DIMENSIONS][QRNG_RESOLUTION]);

////////////////////////////////////////////////////////////////////////////////
// GPU code
////////////////////////////////////////////////////////////////////////////////
extern "C" void initTable_SM10(unsigned int tableCPU[QRNG_DIMENSIONS][QRNG_RESOLUTION]);
extern "C" void tsp_SM10(int NMax, int* distancias, int optimo, int* solucion);
extern "C" void quasirandomGenerator_SM10(float *d_Output, unsigned int seed, unsigned int N);
extern "C" void initTable_SM13(unsigned int tableCPU[QRNG_DIMENSIONS][QRNG_RESOLUTION]);
extern "C" void tsp_SM13(int NMax, int* distancias, int optimo, int* solucion, int* w, int* wPrima);
extern "C" void quasirandomGenerator_SM13(float *d_Output, unsigned int seed, unsigned int N);

const int N = 1048576;

extern "C" void readFile(char *nombre, int *distancias, int *N);

void mostrar(int NMax, int *ruta, int thread){
  printf( "[ " );
  for (int i=0; i<NMax; i++){
        printf("%d ",ruta[thread * NMax + i]);
  }
  printf( "]\n" );
}
int calcularCostoHost(int NMax, int *w, int *distancias, int thread){
  int costo = 0;

  for (int i=0; i<NMax-1; i++){
        int n1 = w[thread * NMax + i];
        int n2 = w[thread * NMax + i + 1];
        costo += distancias[n1 * NMax + n2];
  }
  int n1 = w[thread * NMax];
  int n2 = w[thread * NMax + NMax - 1];
  costo += distancias[n1 * NMax + n2];

  return costo;
}


int main(int argc, char **argv)
{
	if(argc==3)
	{
		//Verificando consistencia
    if(sizeof(INT64) != 8){
        shrLog("sizeof(INT64) != 8\n");
        return 0;
    }

		unsigned int tableCPU[QRNG_DIMENSIONS][QRNG_RESOLUTION];//Tabla de pibotes

	  int *distancias_h, *solucion_h;
    int *distancias_d, *w_d, *wPrima_d;
		int optimo = atoi(argv[2]);
		
		//Leyendo el archivo con los datos de entrada
		FILE *fp =  fopen (argv[1], "r");
		int NMax;		
		fscanf (fp, "%d", &NMax);
		int Nthreads = 1;
		size_t matrizSize  = NMax * NMax * sizeof(int);
    size_t arregloSize = Nthreads * NMax * sizeof(int);
    
		distancias_h = (int*)malloc(matrizSize);

    int a,i,j;
    for(i=0; i<NMax; i++){
      for(j=0; j<=i; j++){
        fscanf (fp, "%d", &a);
        distancias_h[i*NMax+j] = a;
        distancias_h[j*NMax+i] = a;
      }
    }
    fclose(fp);
		//Fin lectura de datos de entrada

    cudaSetDevice( cutGetMaxGflopsDeviceId() );//use device with highest Gflops/s

		cutilSafeCall( cudaMalloc((void **)&distancias_d, matrizSize) );//Reservando espacio en la GPU para matriz de distancias
		cutilSafeCall( cudaMalloc((void **)&w_d, 			   arregloSize) );//Reservando espacio en la GPU para las solucion
		cutilSafeCall( cudaMalloc((void **)&wPrima_d,    arregloSize) );//Reservando espacio en la GPU para una posible solucion
		solucion_h = (int *)malloc(arregloSize);

    initQuasirandomGenerator(tableCPU);
    initTable_SM10(tableCPU);
  
    cutilSafeCall( cudaThreadSynchronize() );

		cutilSafeCall( cudaMemcpy(distancias_d, distancias_h, matrizSize, cudaMemcpyHostToDevice) );

		//Se procede a lanzar el kernel
		tsp_SM13(NMax, distancias_d, optimo, solucion_h, w_d, wPrima_d);
		//el kernel ha finalizado


		for(int t=0; t<Nthreads; t++){
    	//printf("%d  ",calcularCostoHost(N,resultado_h,distancias_h,t));
      mostrar(NMax, solucion_h, t);//muestro la ruta final
		}
		printf("\n");

		
    free(solucion_h);
		free(distancias_h);
    cutilSafeCall( cudaFree(distancias_d) );
    cutilSafeCall( cudaFree(w_d) 					);
    cutilSafeCall( cudaFree(wPrima_d)     );

    cudaThreadExit();
	}
	else
	{
		printf("Error en número de parametros:\n\t./[ejecutable] [problema.tsp] [optimo]\n");
  }
  return 0;

}
